package com.soa.zuul.filter;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.druid.util.StringUtils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.soa.zuul.entity.GatewayHttpApi;
import com.soa.zuul.entity.GatewayService;
import com.soa.zuul.entity.GatewayUser;
import com.soa.zuul.service.FeignLogService;
import com.soa.zuul.service.GatewayHttpApiService;
import com.soa.zuul.service.GatewayServiceService;
import com.soa.zuul.service.UserAuthService;
import com.soa.zuul.util.CodeMsg;
import com.soa.zuul.util.Constant;
import com.soa.zuul.util.Json;
import com.soa.zuul.util.RequestUtil;

/**
 * url参数整理，插件参数初始化。
 * @author zheng
 *
 */
public class RequestUrlParamFilter extends ZuulFilter {
    private static Logger log = LoggerFactory.getLogger(RequestUrlParamFilter.class);
    GatewayHttpApiService gatewayHttpApiService;
    GatewayServiceService gatewayServiceService;
    UserAuthService userAuthService;
    FeignLogService feignLogService;
	public RequestUrlParamFilter(GatewayHttpApiService gatewayHttpApiService,GatewayServiceService gatewayServiceService,UserAuthService userAuthService,FeignLogService feignLogService) {
		this.gatewayHttpApiService = gatewayHttpApiService;
		this.userAuthService = userAuthService;
		this.gatewayServiceService = gatewayServiceService;
		this.feignLogService =feignLogService;
	}
    /**
     * pre：路由之前
     * routing：路由之时
     * post： 路由之后
     * error：发送错误调用
     *
     * @return
     */
    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * filterOrder：过滤的顺序
     *
     * @return
     */
    @Override
    public int filterOrder() {
        return 10;
    }

    /**
     * shouldFilter：这里可以写逻辑判断，是否要过滤，本文true,永远过滤
     *
     * @return
     */
    @Override
    public boolean shouldFilter() {
		RequestContext ctx = RequestContext.getCurrentContext();
		HttpServletRequest request = ctx.getRequest();
		String url=request.getRequestURI();
		ctx.set("isSuccess", true); 
		//健康检查url跳过过滤
		if(url.startsWith("/health") || url.startsWith("/metrics/")|| url.startsWith("/info")|| url.startsWith("/heapdump")|| url.startsWith("/jolokia/")|| url.startsWith("/metrics")
				|| url.startsWith("/env")|| url.startsWith("/dump")|| url.startsWith("/loggers")|| url.startsWith("/logfile")|| url.startsWith("/jolokia")
				|| url.startsWith("/liquibase")|| url.startsWith("/flyway")|| url.startsWith("/auditevents")|| url.startsWith("/gcs/getIp")|| url.startsWith("/favicon.ico")||url.endsWith("css")||url.endsWith("js")||url.endsWith("png")||url.endsWith("jpg")||url.endsWith("gif")){
			ctx.set("isSuccess", false); 
			return false;
		}
		return true;
    }

    /**
     * run：过滤器的具体逻辑。
     * 要把请求参数进行验签（解密）之后传给后续的微服务，首先获取到request，但是在request中只有getParameter()而没有setParameter()方法
     * 所以直接修改url参数不可行，另外在reqeust中虽然可以使用setAttribute(),但是可能由于作用域（request）的不同，一台服务器中才能getAttribute
     * 在这里设置的attribute在后续的微服务中是获取不到的，因此必须考虑另外的方式：即获取请求的输入流，并重写，即重写json参数，
     * ctx.setRequest(new HttpServletRequestWrapper(request) {})，这种方式可重新构造上下文中的request
     *
     * @return
     */
    @Override
    public Object run() {
    	CodeMsg codeMsg = CodeMsg.SUCCESS;
		//路由起始时间
		long permissionStatTime =  System.currentTimeMillis();
        // 获取到request
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        // 请求方法
		String method = request.getMethod();
		String requestURI = request.getRequestURI();
		// 只处理GET方式的URL参数（如果参数未uri?a=&b=1格式,lua语言会导致a获取的值为true）
		if ("GET".equals(method)) {
			Map<String, String[]> paramMap=request.getParameterMap();  
			Map<String, List<String>> requestQueryParams = ctx.getRequestQueryParams();
	    	if (requestQueryParams==null) {
	    		requestQueryParams=new HashMap<>();
	    	}
			for(Entry<String, String[]> entry:paramMap.entrySet()){
				//如果参数值为空，则去掉该参数。。。。。但是ws2http协议，由于需要解析frameker，参数必须存在，所以例外。
				if(!StringUtils.isEmpty(request.getParameter(entry.getKey())) || requestURI.indexOf("ws2http")!=-1){
					ArrayList<String> arrayList = new ArrayList<>();
					arrayList.add(request.getParameter(entry.getKey()));
					requestQueryParams.put(entry.getKey(), arrayList);
				}
			}
	    	ctx.setRequestQueryParams(requestQueryParams);
		}
		
		//获取客户端IP地址
		String ip = RequestUtil.getIpAdrress(request);
		//获取客户端token
		String access_token = RequestUtil.getAccessToken(request);
		String userAgent = request.getHeader("user-agent");
		Map<String, String[]> map = request.getParameterMap();
		String contentType = request.getHeader("Content-Type");
		if((contentType!=null && "multipart/form-data".indexOf(contentType)==-1) || contentType==null){
			ctx.set("params", Json.GSON.toJson(map)); 
		}
		ctx.set("method", method);
		ctx.set("access_token", access_token);
		ctx.set("ip", ip);
		ctx.set("permissionStatTime", permissionStatTime);
		ctx.set("uri", requestURI);
		ctx.set("userAgent", userAgent);
		//根据url查询应用路由
		GatewayHttpApi gatewayHttpApi = gatewayHttpApiService.getGatewayHttpApiByPath(requestURI);
		//找不到应用路由配置直接返回
		if(gatewayHttpApi==null){
			codeMsg = CodeMsg.API_NULL;
			feignLogService.writeLog(Json.GSON.toJson(RequestUtil.SendZuulResponseError(codeMsg, ctx))) ;
			return null;
		}
		//如果为网页代理。则后续所有的过滤器都跳过。日志也不记录了。
		if(Constant.proxyTypePage.equals(gatewayHttpApi.getProxyType())){
			ctx.set("isSuccess", false); 
			return null;
		}
		//根据路由和url查询具体的服务
		GatewayService gatewayService = gatewayServiceService.getGatewayServiceByPath(requestURI,gatewayHttpApi.getId());
		if(!org.springframework.util.StringUtils.isEmpty(access_token)){
			GatewayUser gatewayUser = userAuthService.getGatewayUserByToken(access_token);
			ctx.set("gatewayUser", gatewayUser);
		}
		ctx.set("gatewayHttpApi", gatewayHttpApi);
		ctx.set("gatewayService", gatewayService);
        return null;
    }
} 